import static org.junit.Assert.*;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;


public class VendingMachineTest {

	private static VendingMachine myMachine;
	
	//set up code for test
	@BeforeClass
	public static void setUpBeforeClass() throws Exception 
	{
		CreateNewVendingMachine();
	}

	//tear down code
	@AfterClass
	public static void tearDownAfterClass() throws Exception 
	{
		ClearVendingMachine();
		
	}
	
	//creates a new vending machine for setup
	private static void CreateNewVendingMachine()
	{
		myMachine = new VendingMachine();
	}
	
	//clears the vending machine codes by removing the item from the slot
	@SuppressWarnings("static-access")
	private static void ClearVendingMachine()
	{
		VendingMachineItem myItem = new VendingMachineItem(null,0);
		try
		{
			 myItem = myMachine.getItem(myMachine.A_CODE);
		}
		catch(Exception e)
		{
		
		}
		if(myItem != null)
			myMachine.removeItem(myMachine.A_CODE);
		
		try
		{
			 myItem = myMachine.getItem(myMachine.B_CODE);
		}
		catch(Exception e)
		{
		
		}
		if(myItem != null)
			myMachine.removeItem(myMachine.B_CODE);
		
		try
		{
			 myItem = myMachine.getItem(myMachine.C_CODE);
		}
		catch(Exception e)
		{
		
		}
		if(myItem != null)
			myMachine.removeItem(myMachine.C_CODE);
		
		try
		{
			 myItem = myMachine.getItem(myMachine.D_CODE);
		}
		catch(Exception e)
		{
		
		}
		if(myItem != null)
			myMachine.removeItem(myMachine.D_CODE);
	}
	
	//add an item to the vending machine and is indexed by the caller with an integer
	@SuppressWarnings("static-access")
	private static VendingMachineItem AddMyItemToVendingMachine(int code)
	{
		VendingMachineItem myItem = new VendingMachineItem("Milk Way", .50);
		switch(code)
		{
		case 1:
			myMachine.addItem(myItem, myMachine.A_CODE);
			break;
			
		case 2:
			myMachine.addItem(myItem, myMachine.B_CODE);
			break;
			
		case 3:
			myMachine.addItem(myItem, myMachine.C_CODE);
			break;
			
		case 4:
			myMachine.addItem(myItem, myMachine.D_CODE);
			break;
		
		}
				
		return myItem;
	}
	
	//adds a legitimate item
	@SuppressWarnings("static-access")
	@Test
	public void testAddItem() 
	{
		VendingMachineItem myItem = AddMyItemToVendingMachine(1);
		VendingMachineItem testItem = myMachine.getItem(myMachine.A_CODE);
		assertEquals(myItem,testItem);
	}
	
	//adds an item to an occupied slot - should throw and exception
	@Test(expected = VendingMachineException.class)
	public void AddItemToOccupiedSlot()
	{
		ClearVendingMachine();
		VendingMachineItem myItem = AddMyItemToVendingMachine(1);
		VendingMachineItem nextItem = AddMyItemToVendingMachine(1);
	}
	
	//adds an item with an invalid code - should throw an exception
	@Test(expected = VendingMachineException.class) 
	public void AddItemWithInvalidCode()
	{
		ClearVendingMachine();
		VendingMachineItem myItem = new VendingMachineItem("Baby Ruth", 1.00);
		myMachine.addItem(myItem, "F");
	}
	
	//test removing an item from the machine
	//clear the vending machine and and add an item to the machine
	//get the item from the machine and see if it is the same as the original item
	//remove the item from the machine
	@SuppressWarnings("static-access")
	@Test
	public void testRemoveItem()
	{
		ClearVendingMachine();
		VendingMachineItem myItem = AddMyItemToVendingMachine(2);
		assertEquals(myItem, myMachine.getItem(myMachine.B_CODE));
		VendingMachineItem testItem = myMachine.removeItem(myMachine.B_CODE);
		assertEquals(testItem, myItem);
		
	}
	
	//test for removing an item from a unoccupied slot
	@Test(expected = VendingMachineException.class)
	public void testRemoveItemFromUnoccupiedSlot()
	{
		ClearVendingMachine();
		myMachine.removeItem(myMachine.A_CODE);
	}
	
	//test to remove an item with an invalid code
	@Test(expected = VendingMachineException.class)
	public void testRemoveItemWithInvalidCode()
	{
		ClearVendingMachine();
		myMachine.removeItem("F");
	}

	//test to remove a valid item with an invalid code
	@Test(expected = VendingMachineException.class)
	public void testRemoveValidItemWithInvalidCode()
	{
		ClearVendingMachine();
		VendingMachineItem myItem = AddMyItemToVendingMachine(2);
		myMachine.removeItem("F");
	}
	
	//test to insert money
	//create a new machine and add money, check the balance, add more money check the balance
	@Test
	public void testInsertMoney() 
	{
		//amount >= 0 Postcondition: balance is now the previous balance + amount
		CreateNewVendingMachine();
		//VendingMachineItem myItem = AddMyItemToVendingMachine(3);
		myMachine.insertMoney(.25);
		assertEquals(myMachine.getBalance(), 0.25, 001);
		myMachine.insertMoney(.25);
		assertEquals(myMachine.getBalance(), 0.50, 001);
		myMachine.insertMoney(1.25);
		assertEquals(myMachine.getBalance(), 1.75, .001);
	
	}
	
	//test to insert a zero amount
	//this test should fail as according to the docs, inserting 0.0 as an insert should throw an error
	@Test
	public void testInserMoneyZeroAmount()
	{
		CreateNewVendingMachine();  //sets the balance to Initial balance
		myMachine.insertMoney(0.0);
		assertTrue(myMachine.getBalance()> 0.0);
		
	}

	//inserting an negative amount into the insert money method
	@Test(expected = VendingMachineException.class)
	public void testInserMoneyNegAmount()
	{
		CreateNewVendingMachine();  //sets the balance to Initial balance
		myMachine.insertMoney(-1.25);
		
	}
	
	//test to get the balance
	//create a new machinea and add a new item with a price of $.50
	//insert some money and get the balance, check to see if the balance is correct
	//make a purchase and see if the purchase works and check to see if the balance is correct 
	@SuppressWarnings("static-access")
	@Test
	public void testGetBalance() 
	{
		CreateNewVendingMachine();
		VendingMachineItem myItem = AddMyItemToVendingMachine(3);
		myMachine.insertMoney(.75);
		assertEquals(myMachine.getBalance(), 0.75, 001);
		
		assertTrue(myMachine.makePurchase(myMachine.C_CODE));  
		assertEquals(myMachine.getBalance(), 0.25, .001);
	}
	//test to make purchase
	//create a new machine and add an item to the machine
	//insert some money and make a purchase
	@SuppressWarnings("static-access")
	@Test
	public void testMakePurchase() 
	{
		CreateNewVendingMachine();
		VendingMachineItem myItem = AddMyItemToVendingMachine(3);
		myMachine.insertMoney(.75);
		assertTrue(myMachine.makePurchase(myMachine.C_CODE));
				
	}
	
	//test to see if you can make a purchase with not the correct amout of money
	@SuppressWarnings("static-access")
	@Test
	public void testMakePurchaseNotEnoughMoney() 
	{
		CreateNewVendingMachine();
		VendingMachineItem myItem = AddMyItemToVendingMachine(3);
		myMachine.insertMoney(.25);
		assertFalse(myMachine.makePurchase(myMachine.C_CODE));
				
	}
	
	//test to see if you can make a purchase on initialization and 0.0 money
	//This test should fail
	//create a new machine and add an item wihtout a name and 0 cost
	//add the item insert 0 money and see if you can make a purchase
	@Test
	public void testMakePurchaseOnInitalization()
	{
		VendingMachine myMachine = new VendingMachine();
		VendingMachineItem myItem = new VendingMachineItem("",0.0);
		myMachine.addItem(myItem, myMachine.D_CODE);
		myMachine.insertMoney(0);
		//this should assert false as you should not be able to make a purchase with all zero values
		assertFalse(myMachine.makePurchase(myMachine.D_CODE));
		
		
	}

	//test to see if you can make a purchase on an empty slot
	//create a new machine and insert some money, see if you can make a purchase
	@SuppressWarnings("static-access")
	@Test
	public void testMakePurchaseEmptySlot() 
	{
		CreateNewVendingMachine();
		//VendingMachineItem myItem = AddMyItemToVendingMachine(3);
		myMachine.insertMoney(.75);
		assertFalse(myMachine.makePurchase(myMachine.C_CODE));
				
	}
	
	//test to return the change
	//create a new machine and add a legit item to the machine
	//insert some money and get return the change
	//see if you can make a purchase and see if we can return more change
	//add more money and make a purchase
	//see if the correct change is returned and see if there is a 0 balance
	//
	@Test
	public void testReturnChange() 
	{
		CreateNewVendingMachine();
		AddMyItemToVendingMachine(3);
		myMachine.insertMoney(.75);
		assertEquals(myMachine.returnChange(), 0.75, .001); //returns the change without making a purchase
		assertFalse(myMachine.makePurchase(myMachine.C_CODE)); //see if you can make a purchase
		assertEquals(myMachine.returnChange(), 0.00, .001); //should be zero we haven't put new money in
		myMachine.insertMoney(.75);
		myMachine.makePurchase(myMachine.C_CODE);
		assertEquals(myMachine.returnChange(), 0.25, .001); //return $.25
		assertEquals(myMachine.getBalance(), 0.0, .001);
	}

}
